1 Introducción

2 Fundamento teórico: eliminación de ruido con wavelets

3 Funciones de programacion empleadas

4 Desarrollo y resultados

Antes de comenzar, instalamos y/o cargamos todos los paquetes requeridos.

if (!require("pacman")) install.packages("pacman")
library(pacman)
p_load(imager, wavethresh, ggplot2, dplyr, SpatialPack, waveslim, EBImage, stringr, jpeg)

Comenzamos cargando y visualizando las fotografías a emplear.

images_path <- list.files("./fotos", full.names = TRUE)

nombres_images <- str_remove_all(string = str_remove_all(string = images_path, pattern = "./fotos/"), pattern = "\\.JPG|\\.jpg")

images <- lapply(images_path, readJPEG) # Cargamos las imágenes

# Nota: cambie de load.image a readJPEG pq asi ya no hace falta usar el drop para quitar lo de cimg, sale bien directamente
names(images) <- nombres_images
# Rotamos algunas de las fotos para una visualización más uniforme
fotos_a_girar <- c("1", "2", "3", "4")
images_rotadas <- lapply(images[fotos_a_girar], aperm, perm = c(2, 1, 3))


for (i in fotos_a_girar) {
  images_rotadas[[i]] < images_rotadas[[i]][dim(images_rotadas[[i]])[1]:1, , ]
}

images[fotos_a_girar] <- images_rotadas
rm(images_rotadas)
rm(fotos_a_girar)
# Visualizamos las imagenes originales

par(mfrow = c(2, 3), mar = c(1, 1, 1, 1))

for (img in nombres_images) {
  display(Image(images[[img]], colormode = "Color"), method = "r")
}

4.1 Inclusión de ruido sintético en las imágenes

# Definición de tipos de ruido

NOISE_TYPES <- list(
  gaussian = list(
    generator = function(channel, params) {
      # Desviación estándar del ruido con un valor predeterminado
      noise_std_dev <- params$std_dev %||% 0.5

      # Generación de ruido gaussiano
      noise <- array(
        rnorm(length(channel), mean = 0, sd = noise_std_dev),
        dim = dim(channel)
      )

      # Asegurar que los valores estén entre 0 y 1
      pmax(0, pmin(1, channel + noise))
    }
  ),
  sinusoidal_high = list(
    generator = function(channel, params) {
      # Frecuencia y amplitud del ruido sinusoidal de alta frecuencia
      frequency <- params$frequency %||% 25
      amplitude <- params$amplitude %||% 0.2

      # Generación de ruido sinusoidal
      height <- dim(channel)[1]
      width <- dim(channel)[2]
      x <- seq(0, 2 * pi, length.out = width)
      y <- seq(0, 2 * pi, length.out = height)
      noise_grid <- outer(sin(x * frequency), sin(y * frequency))

      # Aplicar el ruido
      noise <- array(noise_grid * amplitude, dim = dim(channel))
      pmax(0, pmin(1, channel + noise))
    }
  ),
  sinusoidal_low = list(
    generator = function(channel, params) {
      # Frecuencia y amplitud del ruido sinusoidal de baja frecuencia
      frequency <- params$frequency %||% 2
      amplitude <- params$amplitude %||% 0.2

      # Generación de ruido sinusoidal
      height <- dim(channel)[1]
      width <- dim(channel)[2]
      x <- seq(0, 2 * pi, length.out = width)
      y <- seq(0, 2 * pi, length.out = height)
      noise_grid <- outer(sin(x * frequency), sin(y * frequency))

      # Aplicar el ruido
      noise <- array(noise_grid * amplitude, dim = dim(channel))
      pmax(0, pmin(1, channel + noise))
    }
  ),
  salt_pepper = list(
    generator = function(channel, params) {
      # Proporción de píxeles afectados por el ruido de sal y pimienta
      epsilon <- params$epsilon %||% 0.2

      # Generación de ruido
      noise <- matrix(sample(c(0, 1, NA), length(channel), replace = TRUE, prob = c(epsilon / 2, epsilon / 2, 1 - epsilon)),
        nrow = dim(channel)[1], ncol = dim(channel)[2]
      )
      channel[!is.na(noise)] <- noise[!is.na(noise)]
      channel
    }
  ),
  gamma = list(
    generator = function(channel, params) {
      # Ruido multiplicativo gamma con parámetro de dispersión
      looks <- params$looks %||% 2
      noise <- array(rgamma(length(channel), shape = looks, scale = 1 / looks), dim = dim(channel))
      pmax(0, pmin(1, channel * noise))
    }
  ),
  uniform_multiplicative = list(
    generator = function(channel, params) {
      # Ruido multiplicativo uniforme
      looks <- params$looks %||% 2
      noise_channel <- SpatialPack::imnoise(
        img = channel,
        type = "speckle",
        looks = looks
      )
      pmax(0, pmin(1, noise_channel))
    }
  )
)

# Función para añadir ruido a una imagen
add_noise_to_image <- function(image_name, noise_type, noise_params = list(),plot=FALSE) {
  # Verificar si la imagen existe en la lista
  if (!image_name %in% names(images)) {
    stop("La imagen con este nombre no se encuentra en la lista 'images'")
  }

  # Verificar el tipo de ruido
  if (!noise_type %in% names(NOISE_TYPES)) {
    stop(
      "El tipo de ruido es desconocido. Tipos disponibles: ",
      paste(names(NOISE_TYPES), collapse = ", ")
    )
  }

  # Obtener la imagen original de la lista
  original_image <- images[[image_name]]

  # Convertir la imagen a un array si es necesario
  image_array <- as.array(original_image)

  # Aplicar ruido a cada canal
  noisy_channels <- lapply(1:3, function(i) {
    channel <- image_array[, , i]
    NOISE_TYPES[[noise_type]]$generator(channel, noise_params)
  })

  # Crear la imagen con ruido
  noisy_image_array <- array(
    unlist(noisy_channels),
    dim = dim(image_array)
  )

 # Visualizar si se ha indicado
  if (plot == TRUE){
  layout(matrix(1:2, 1, 2))
  plot(Image(original_image, colormode = "Color"))
  title("Original")
  plot(Image((noisy_image_array), colormode = "Color"))
  title(paste("Ruido:", noise_type))}
  
  return(noisy_image_array)
}

# Aplicar los diferentes tipos de ruido a cada imagen
#add_noise_to_image("1", "gaussian", list(std_dev = 0.3))
#add_noise_to_image("2", "sinusoidal_high", list(frequency = 25, amplitude = 0.2))
#add_noise_to_image("3", "sinusoidal_low", list(frequency = 2, amplitude = 0.2))
#add_noise_to_image("4", "salt_pepper", list(epsilon = 0.1))
#add_noise_to_image("5", "gamma", list(looks = 2))
#add_noise_to_image("5", "uniform_multiplicative", list(looks = 2))

4.2 Función imwd

Función para hacer cuadrada la imagen.

Para aplicar el algoritmo de Deformación Iterativa de Mallat (IMWD), es necesario que la imagen tenga una forma cuadrada. Dado que muchas imágenes no son cuadradas, es necesario convertirlas antes de aplicar el algoritmo.

A continuación, se presenta una función de pre-procesamiento, que ajusta cualquier imagen rectangular a un tamaño cuadrado, manteniendo sus proporciones originales al agregar relleno (fondo blanco) si es necesario. Esta transformación asegura que la imagen sea compatible con el algoritmo IMWD.

hacer_cuadrada_potencia_2 <- function(imagen) {
  n_filas <- dim(imagen)[1]
  n_columnas <- dim(imagen)[2]
  
  nuevo_tamano <- max(n_filas, n_columnas)
  
  siguiente_potencia_2 <- 2^ceiling(log2(nuevo_tamano))
  
  imagen_cuadrada <- array(0, dim = c(siguiente_potencia_2, siguiente_potencia_2, dim(imagen)[3])) 
  
  imagen_cuadrada[1:n_filas, 1:n_columnas, ] <- imagen
  
  return(imagen_cuadrada)
}

Cargar imagenes en la función cuadrada

Aplicamos la función en 3 fotos

fotos_cuadradas <- lapply(imagen_noise, hacer_cuadrada_potencia_2)

Imagen Original

[1] "1 Noise: gaussian"
[1] "2 Noise: gamma"

[1] "3 Noise: unif"

Cuando uso fotos de mayor calidad solo acepta hasta 2, con 3 aparece esto: Error: vector memory limit of 16.0 Gb reached, see mem.maxVSize()

El umbral “universal”, propuesta por Donoho y Johnstone, es un método utilizado en la eliminación de ruido en señales e imágenes mediante transformadas de wavelet. Esta estrategia calcula el umbral aplicado a los coeficientes de wavelet en función del tamaño de la señal y una estimación del nivel de ruido. La fórmula del umbral “universal” es \[ \sigma \sqrt{2 \log n}\] donde \(\sigma\) es una estimación del ruido y n es el número de muestras o elementos de la señal.

Imagenes sin ruido

Error in slot(prueba, ".Data")[1:1600, 1:1066, , drop = FALSE] : 
  subscript out of bounds

Ruido gaussiano

Imagen con ruido Gaussiano original

  1. Imagen Cortada sin filtro

  2. Ajusta el contraste de la imagen, incrementando la diferencia entre los píxeles claros y oscuros.

  3. Imagen con filtro paso alto

4.3 Función denoise…

5 Conclusiones

LS0tCnRpdGxlOiAiRWxpbWluYWNpw7NuIGRlIHJ1aWRvIGVuIGltw6FnZW5lcyBjb24gd2F2ZWxldHMuIgpzdWJ0aXRsZTogIkFuw6FsaXNpcyBkZSBzZcOxYWxlcyIKYXV0aG9yOiAiR3J1cG8gRTogQWxlamFuZHJhIFZlbmVnYXMsIFJlYmVjYSBDb21wYW55LCBNYXJ0YSBNZWRpbmEsIEFsZWphbmRybyBDb3JuZWxpbyB5IElsaWEgWmhpZ2FyZXYuIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgZWNobzogdHJ1ZQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICB0aGVtZTogbHVtZW4KICAgIHRvYzogdHJ1ZQogIHBkZl9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAzCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICBib29rZG93bjo6cGRmX2RvY3VtZW50MjoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAzCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIGZpZ3VyZV9jYXB0aW9uOiAiRmlndXJhIiAjIFJlZmVyZW5jaWFzIGVuIGNhc3RlbGxhbm8KICAgIHRhYmxlX2NhcHRpb246ICJUYWJsYSIKICBodG1sX25vdGVib29rOgogICAgZWNobzogdHJ1ZQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICB0b2M6IHRydWUKICBib29rZG93bjo6aHRtbF9kb2N1bWVudDI6CiAgICBlY2hvOiB0cnVlCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRoZW1lOiBzcGFjZWxhYgogICAgdG9jOiB0cnVlCiAgICBmaWd1cmVfY2FwdGlvbjogIkZpZ3VyYSIKICAgIHRhYmxlX2NhcHRpb246ICJUYWJsYSIKYWx3YXlzX2FsbG93X2h0bWw6IHRydWUKcGFyYW1zOgogIGxhbmc6IEVTCmxhbmc6ICJgciBzd2l0Y2gocGFyYW1zJGxhbmcsIEVTID0gJ2VzLUVTJywgRU4gPSAnZW4tVVMnKWAiCmxhbmd1YWdlOgogIGxhYmVsOgogICAgZmlnOiAnRmlndXJhICcKICAgIHRhYjogJ1RhYmxhICcKICAgIGVxOiAnRWN1YWNpw7NuICcKICAgIHRobTogJ1Rlb3JlbWEgJwogICAgbGVtOiAnTGVtYSAnCiAgICBkZWY6ICdEZWZpbmljacOzbiAnCiAgICBjb3I6ICdDb3JvbGFyaW8gJwogICAgcHJwOiAnUHJvcG9zaWNpw7NuICcKICAgIGV4bTogJ0VqZW1wbG8gJwogICAgZXhyOiAnRWplcmNpY2lvICcKICAgIHByb29mOiAnRGVtb3N0cmFjacOzbi4gJwogICAgcmVtYXJrOiAnTm90YTogJwogICAgc29sdXRpb246ICdTb2x1Y2nDs24uICcKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpybShsaXN0ID0gbHMoKSkKYGBgCgojIEludHJvZHVjY2nDs24KCiMgRnVuZGFtZW50byB0ZcOzcmljbzogZWxpbWluYWNpw7NuIGRlIHJ1aWRvIGNvbiB3YXZlbGV0cwoKIyBGdW5jaW9uZXMgZGUgcHJvZ3JhbWFjaW9uIGVtcGxlYWRhcwoKIyBEZXNhcnJvbGxvIHkgcmVzdWx0YWRvcwoKQW50ZXMgZGUgY29tZW56YXIsIGluc3RhbGFtb3MgeS9vIGNhcmdhbW9zIHRvZG9zIGxvcyBwYXF1ZXRlcyByZXF1ZXJpZG9zLgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFfQppZiAoIXJlcXVpcmUoInBhY21hbiIpKSBpbnN0YWxsLnBhY2thZ2VzKCJwYWNtYW4iKQpsaWJyYXJ5KHBhY21hbikKcF9sb2FkKGltYWdlciwgd2F2ZXRocmVzaCwgZ2dwbG90MiwgZHBseXIsIFNwYXRpYWxQYWNrLCB3YXZlc2xpbSwgRUJJbWFnZSwgc3RyaW5nciwganBlZykKYGBgCgpDb21lbnphbW9zIGNhcmdhbmRvIHkgdmlzdWFsaXphbmRvIGxhcyBmb3RvZ3JhZsOtYXMgYSBlbXBsZWFyLgpgYGB7cn0KaW1hZ2VzX3BhdGggPC0gbGlzdC5maWxlcygiLi9mb3RvcyIsIGZ1bGwubmFtZXMgPSBUUlVFKQoKbm9tYnJlc19pbWFnZXMgPC0gc3RyX3JlbW92ZV9hbGwoc3RyaW5nID0gc3RyX3JlbW92ZV9hbGwoc3RyaW5nID0gaW1hZ2VzX3BhdGgsIHBhdHRlcm4gPSAiLi9mb3Rvcy8iKSwgcGF0dGVybiA9ICJcXC5KUEd8XFwuanBnIikKCmltYWdlcyA8LSBsYXBwbHkoaW1hZ2VzX3BhdGgsIHJlYWRKUEVHKSAjIENhcmdhbW9zIGxhcyBpbcOhZ2VuZXMKCiMgTm90YTogY2FtYmllIGRlIGxvYWQuaW1hZ2UgYSByZWFkSlBFRyBwcSBhc2kgeWEgbm8gaGFjZSBmYWx0YSB1c2FyIGVsIGRyb3AgcGFyYSBxdWl0YXIgbG8gZGUgY2ltZywgc2FsZSBiaWVuIGRpcmVjdGFtZW50ZQpuYW1lcyhpbWFnZXMpIDwtIG5vbWJyZXNfaW1hZ2VzCmBgYAoKYGBge3J9CiMgUm90YW1vcyBhbGd1bmFzIGRlIGxhcyBmb3RvcyBwYXJhIHVuYSB2aXN1YWxpemFjacOzbiBtw6FzIHVuaWZvcm1lCmZvdG9zX2FfZ2lyYXIgPC0gYygiMSIsICIyIiwgIjMiLCAiNCIpCmltYWdlc19yb3RhZGFzIDwtIGxhcHBseShpbWFnZXNbZm90b3NfYV9naXJhcl0sIGFwZXJtLCBwZXJtID0gYygyLCAxLCAzKSkKCgpmb3IgKGkgaW4gZm90b3NfYV9naXJhcikgewogIGltYWdlc19yb3RhZGFzW1tpXV0gPCBpbWFnZXNfcm90YWRhc1tbaV1dW2RpbShpbWFnZXNfcm90YWRhc1tbaV1dKVsxXToxLCAsIF0KfQoKaW1hZ2VzW2ZvdG9zX2FfZ2lyYXJdIDwtIGltYWdlc19yb3RhZGFzCnJtKGltYWdlc19yb3RhZGFzKQpybShmb3Rvc19hX2dpcmFyKQpgYGAKCgpgYGB7cn0KIyBWaXN1YWxpemFtb3MgbGFzIGltYWdlbmVzIG9yaWdpbmFsZXMKCnBhcihtZnJvdyA9IGMoMiwgMyksIG1hciA9IGMoMSwgMSwgMSwgMSkpCgpmb3IgKGltZyBpbiBub21icmVzX2ltYWdlcykgewogIGRpc3BsYXkoSW1hZ2UoaW1hZ2VzW1tpbWddXSwgY29sb3Jtb2RlID0gIkNvbG9yIiksIG1ldGhvZCA9ICJyIikKfQpgYGAKCiMjIEluY2x1c2nDs24gZGUgcnVpZG8gc2ludMOpdGljbyBlbiBsYXMgaW3DoWdlbmVzCgpgYGB7ciwgZmlnLmhlaWdodCA9IDR9CiMgRGVmaW5pY2nDs24gZGUgdGlwb3MgZGUgcnVpZG8KCk5PSVNFX1RZUEVTIDwtIGxpc3QoCiAgZ2F1c3NpYW4gPSBsaXN0KAogICAgZ2VuZXJhdG9yID0gZnVuY3Rpb24oY2hhbm5lbCwgcGFyYW1zKSB7CiAgICAgICMgRGVzdmlhY2nDs24gZXN0w6FuZGFyIGRlbCBydWlkbyBjb24gdW4gdmFsb3IgcHJlZGV0ZXJtaW5hZG8KICAgICAgbm9pc2Vfc3RkX2RldiA8LSBwYXJhbXMkc3RkX2RldiAlfHwlIDAuNQoKICAgICAgIyBHZW5lcmFjacOzbiBkZSBydWlkbyBnYXVzc2lhbm8KICAgICAgbm9pc2UgPC0gYXJyYXkoCiAgICAgICAgcm5vcm0obGVuZ3RoKGNoYW5uZWwpLCBtZWFuID0gMCwgc2QgPSBub2lzZV9zdGRfZGV2KSwKICAgICAgICBkaW0gPSBkaW0oY2hhbm5lbCkKICAgICAgKQoKICAgICAgIyBBc2VndXJhciBxdWUgbG9zIHZhbG9yZXMgZXN0w6luIGVudHJlIDAgeSAxCiAgICAgIHBtYXgoMCwgcG1pbigxLCBjaGFubmVsICsgbm9pc2UpKQogICAgfQogICksCiAgc2ludXNvaWRhbF9oaWdoID0gbGlzdCgKICAgIGdlbmVyYXRvciA9IGZ1bmN0aW9uKGNoYW5uZWwsIHBhcmFtcykgewogICAgICAjIEZyZWN1ZW5jaWEgeSBhbXBsaXR1ZCBkZWwgcnVpZG8gc2ludXNvaWRhbCBkZSBhbHRhIGZyZWN1ZW5jaWEKICAgICAgZnJlcXVlbmN5IDwtIHBhcmFtcyRmcmVxdWVuY3kgJXx8JSAyNQogICAgICBhbXBsaXR1ZGUgPC0gcGFyYW1zJGFtcGxpdHVkZSAlfHwlIDAuMgoKICAgICAgIyBHZW5lcmFjacOzbiBkZSBydWlkbyBzaW51c29pZGFsCiAgICAgIGhlaWdodCA8LSBkaW0oY2hhbm5lbClbMV0KICAgICAgd2lkdGggPC0gZGltKGNoYW5uZWwpWzJdCiAgICAgIHggPC0gc2VxKDAsIDIgKiBwaSwgbGVuZ3RoLm91dCA9IHdpZHRoKQogICAgICB5IDwtIHNlcSgwLCAyICogcGksIGxlbmd0aC5vdXQgPSBoZWlnaHQpCiAgICAgIG5vaXNlX2dyaWQgPC0gb3V0ZXIoc2luKHggKiBmcmVxdWVuY3kpLCBzaW4oeSAqIGZyZXF1ZW5jeSkpCgogICAgICAjIEFwbGljYXIgZWwgcnVpZG8KICAgICAgbm9pc2UgPC0gYXJyYXkobm9pc2VfZ3JpZCAqIGFtcGxpdHVkZSwgZGltID0gZGltKGNoYW5uZWwpKQogICAgICBwbWF4KDAsIHBtaW4oMSwgY2hhbm5lbCArIG5vaXNlKSkKICAgIH0KICApLAogIHNpbnVzb2lkYWxfbG93ID0gbGlzdCgKICAgIGdlbmVyYXRvciA9IGZ1bmN0aW9uKGNoYW5uZWwsIHBhcmFtcykgewogICAgICAjIEZyZWN1ZW5jaWEgeSBhbXBsaXR1ZCBkZWwgcnVpZG8gc2ludXNvaWRhbCBkZSBiYWphIGZyZWN1ZW5jaWEKICAgICAgZnJlcXVlbmN5IDwtIHBhcmFtcyRmcmVxdWVuY3kgJXx8JSAyCiAgICAgIGFtcGxpdHVkZSA8LSBwYXJhbXMkYW1wbGl0dWRlICV8fCUgMC4yCgogICAgICAjIEdlbmVyYWNpw7NuIGRlIHJ1aWRvIHNpbnVzb2lkYWwKICAgICAgaGVpZ2h0IDwtIGRpbShjaGFubmVsKVsxXQogICAgICB3aWR0aCA8LSBkaW0oY2hhbm5lbClbMl0KICAgICAgeCA8LSBzZXEoMCwgMiAqIHBpLCBsZW5ndGgub3V0ID0gd2lkdGgpCiAgICAgIHkgPC0gc2VxKDAsIDIgKiBwaSwgbGVuZ3RoLm91dCA9IGhlaWdodCkKICAgICAgbm9pc2VfZ3JpZCA8LSBvdXRlcihzaW4oeCAqIGZyZXF1ZW5jeSksIHNpbih5ICogZnJlcXVlbmN5KSkKCiAgICAgICMgQXBsaWNhciBlbCBydWlkbwogICAgICBub2lzZSA8LSBhcnJheShub2lzZV9ncmlkICogYW1wbGl0dWRlLCBkaW0gPSBkaW0oY2hhbm5lbCkpCiAgICAgIHBtYXgoMCwgcG1pbigxLCBjaGFubmVsICsgbm9pc2UpKQogICAgfQogICksCiAgc2FsdF9wZXBwZXIgPSBsaXN0KAogICAgZ2VuZXJhdG9yID0gZnVuY3Rpb24oY2hhbm5lbCwgcGFyYW1zKSB7CiAgICAgICMgUHJvcG9yY2nDs24gZGUgcMOteGVsZXMgYWZlY3RhZG9zIHBvciBlbCBydWlkbyBkZSBzYWwgeSBwaW1pZW50YQogICAgICBlcHNpbG9uIDwtIHBhcmFtcyRlcHNpbG9uICV8fCUgMC4yCgogICAgICAjIEdlbmVyYWNpw7NuIGRlIHJ1aWRvCiAgICAgIG5vaXNlIDwtIG1hdHJpeChzYW1wbGUoYygwLCAxLCBOQSksIGxlbmd0aChjaGFubmVsKSwgcmVwbGFjZSA9IFRSVUUsIHByb2IgPSBjKGVwc2lsb24gLyAyLCBlcHNpbG9uIC8gMiwgMSAtIGVwc2lsb24pKSwKICAgICAgICBucm93ID0gZGltKGNoYW5uZWwpWzFdLCBuY29sID0gZGltKGNoYW5uZWwpWzJdCiAgICAgICkKICAgICAgY2hhbm5lbFshaXMubmEobm9pc2UpXSA8LSBub2lzZVshaXMubmEobm9pc2UpXQogICAgICBjaGFubmVsCiAgICB9CiAgKSwKICBnYW1tYSA9IGxpc3QoCiAgICBnZW5lcmF0b3IgPSBmdW5jdGlvbihjaGFubmVsLCBwYXJhbXMpIHsKICAgICAgIyBSdWlkbyBtdWx0aXBsaWNhdGl2byBnYW1tYSBjb24gcGFyw6FtZXRybyBkZSBkaXNwZXJzacOzbgogICAgICBsb29rcyA8LSBwYXJhbXMkbG9va3MgJXx8JSAyCiAgICAgIG5vaXNlIDwtIGFycmF5KHJnYW1tYShsZW5ndGgoY2hhbm5lbCksIHNoYXBlID0gbG9va3MsIHNjYWxlID0gMSAvIGxvb2tzKSwgZGltID0gZGltKGNoYW5uZWwpKQogICAgICBwbWF4KDAsIHBtaW4oMSwgY2hhbm5lbCAqIG5vaXNlKSkKICAgIH0KICApLAogIHVuaWZvcm1fbXVsdGlwbGljYXRpdmUgPSBsaXN0KAogICAgZ2VuZXJhdG9yID0gZnVuY3Rpb24oY2hhbm5lbCwgcGFyYW1zKSB7CiAgICAgICMgUnVpZG8gbXVsdGlwbGljYXRpdm8gdW5pZm9ybWUKICAgICAgbG9va3MgPC0gcGFyYW1zJGxvb2tzICV8fCUgMgogICAgICBub2lzZV9jaGFubmVsIDwtIFNwYXRpYWxQYWNrOjppbW5vaXNlKAogICAgICAgIGltZyA9IGNoYW5uZWwsCiAgICAgICAgdHlwZSA9ICJzcGVja2xlIiwKICAgICAgICBsb29rcyA9IGxvb2tzCiAgICAgICkKICAgICAgcG1heCgwLCBwbWluKDEsIG5vaXNlX2NoYW5uZWwpKQogICAgfQogICkKKQoKIyBGdW5jacOzbiBwYXJhIGHDsWFkaXIgcnVpZG8gYSB1bmEgaW1hZ2VuCmFkZF9ub2lzZV90b19pbWFnZSA8LSBmdW5jdGlvbihpbWFnZV9uYW1lLCBub2lzZV90eXBlLCBub2lzZV9wYXJhbXMgPSBsaXN0KCkscGxvdD1GQUxTRSkgewogICMgVmVyaWZpY2FyIHNpIGxhIGltYWdlbiBleGlzdGUgZW4gbGEgbGlzdGEKICBpZiAoIWltYWdlX25hbWUgJWluJSBuYW1lcyhpbWFnZXMpKSB7CiAgICBzdG9wKCJMYSBpbWFnZW4gY29uIGVzdGUgbm9tYnJlIG5vIHNlIGVuY3VlbnRyYSBlbiBsYSBsaXN0YSAnaW1hZ2VzJyIpCiAgfQoKICAjIFZlcmlmaWNhciBlbCB0aXBvIGRlIHJ1aWRvCiAgaWYgKCFub2lzZV90eXBlICVpbiUgbmFtZXMoTk9JU0VfVFlQRVMpKSB7CiAgICBzdG9wKAogICAgICAiRWwgdGlwbyBkZSBydWlkbyBlcyBkZXNjb25vY2lkby4gVGlwb3MgZGlzcG9uaWJsZXM6ICIsCiAgICAgIHBhc3RlKG5hbWVzKE5PSVNFX1RZUEVTKSwgY29sbGFwc2UgPSAiLCAiKQogICAgKQogIH0KCiAgIyBPYnRlbmVyIGxhIGltYWdlbiBvcmlnaW5hbCBkZSBsYSBsaXN0YQogIG9yaWdpbmFsX2ltYWdlIDwtIGltYWdlc1tbaW1hZ2VfbmFtZV1dCgogICMgQ29udmVydGlyIGxhIGltYWdlbiBhIHVuIGFycmF5IHNpIGVzIG5lY2VzYXJpbwogIGltYWdlX2FycmF5IDwtIGFzLmFycmF5KG9yaWdpbmFsX2ltYWdlKQoKICAjIEFwbGljYXIgcnVpZG8gYSBjYWRhIGNhbmFsCiAgbm9pc3lfY2hhbm5lbHMgPC0gbGFwcGx5KDE6MywgZnVuY3Rpb24oaSkgewogICAgY2hhbm5lbCA8LSBpbWFnZV9hcnJheVssICwgaV0KICAgIE5PSVNFX1RZUEVTW1tub2lzZV90eXBlXV0kZ2VuZXJhdG9yKGNoYW5uZWwsIG5vaXNlX3BhcmFtcykKICB9KQoKICAjIENyZWFyIGxhIGltYWdlbiBjb24gcnVpZG8KICBub2lzeV9pbWFnZV9hcnJheSA8LSBhcnJheSgKICAgIHVubGlzdChub2lzeV9jaGFubmVscyksCiAgICBkaW0gPSBkaW0oaW1hZ2VfYXJyYXkpCiAgKQoKICMgVmlzdWFsaXphciBzaSBzZSBoYSBpbmRpY2FkbwogIGlmIChwbG90ID09IFRSVUUpewogIGxheW91dChtYXRyaXgoMToyLCAxLCAyKSkKICBwbG90KEltYWdlKG9yaWdpbmFsX2ltYWdlLCBjb2xvcm1vZGUgPSAiQ29sb3IiKSkKICB0aXRsZSgiT3JpZ2luYWwiKQogIHBsb3QoSW1hZ2UoKG5vaXN5X2ltYWdlX2FycmF5KSwgY29sb3Jtb2RlID0gIkNvbG9yIikpCiAgdGl0bGUocGFzdGUoIlJ1aWRvOiIsIG5vaXNlX3R5cGUpKX0KICAKICByZXR1cm4obm9pc3lfaW1hZ2VfYXJyYXkpCn0KCmBgYAoKCmBgYHtyfQoKIyBBcGxpY2FyIGxvcyBkaWZlcmVudGVzIHRpcG9zIGRlIHJ1aWRvIGEgY2FkYSBpbWFnZW4KI2FkZF9ub2lzZV90b19pbWFnZSgiMSIsICJnYXVzc2lhbiIsIGxpc3Qoc3RkX2RldiA9IDAuMykpCiNhZGRfbm9pc2VfdG9faW1hZ2UoIjIiLCAic2ludXNvaWRhbF9oaWdoIiwgbGlzdChmcmVxdWVuY3kgPSAyNSwgYW1wbGl0dWRlID0gMC4yKSkKI2FkZF9ub2lzZV90b19pbWFnZSgiMyIsICJzaW51c29pZGFsX2xvdyIsIGxpc3QoZnJlcXVlbmN5ID0gMiwgYW1wbGl0dWRlID0gMC4yKSkKI2FkZF9ub2lzZV90b19pbWFnZSgiNCIsICJzYWx0X3BlcHBlciIsIGxpc3QoZXBzaWxvbiA9IDAuMSkpCiNhZGRfbm9pc2VfdG9faW1hZ2UoIjUiLCAiZ2FtbWEiLCBsaXN0KGxvb2tzID0gMikpCiNhZGRfbm9pc2VfdG9faW1hZ2UoIjUiLCAidW5pZm9ybV9tdWx0aXBsaWNhdGl2ZSIsIGxpc3QobG9va3MgPSAyKSkKYGBgCgoKIyMgRnVuY2nDs24gaW13ZAoKYGBge3IsZWNobz1GQUxTRX0KIyBhZ3JlZ2FyIHJ1aWRvIGEgaW1hZ2VuZXMKZ2F1c3NpYW5fbm9pc2VfNTwtYWRkX25vaXNlX3RvX2ltYWdlKCI1IiwgImdhdXNzaWFuIiwgbGlzdChzdGRfZGV2ID0gMC41KSkKZ2FtbWFfbm9pc2VfNTwtYWRkX25vaXNlX3RvX2ltYWdlKCI1IiwgImdhbW1hIiwgbGlzdChsb29rcyA9IDIpKQp1bmlmX25vaXNlXzU8LWFkZF9ub2lzZV90b19pbWFnZSgiNSIsICJ1bmlmb3JtX211bHRpcGxpY2F0aXZlIiwgbGlzdChsb29rcyA9IDIpKQpgYGAKCgoKYGBge3IsZWNobz1GQUxTRX0KI3Byb2JhciBjb24gMyAKaW1hZ2VuX25vaXNlIDwtIGxpc3QoZ2F1c3NpYW5fbm9pc2VfNSwgZ2FtbWFfbm9pc2VfNSwgdW5pZl9ub2lzZV81KQpuYW1lcyhpbWFnZW5fbm9pc2UpIDwtIGMoJzEgTm9pc2U6IGdhdXNzaWFuJywgJzIgTm9pc2U6IGdhbW1hJywgJzMgTm9pc2U6IHVuaWYnKQoKYGBgCgoKKipGdW5jacOzbiBwYXJhIGhhY2VyIGN1YWRyYWRhIGxhIGltYWdlbi4qKgoKUGFyYSBhcGxpY2FyIGVsIGFsZ29yaXRtbyBkZSBEZWZvcm1hY2nDs24gSXRlcmF0aXZhIGRlIE1hbGxhdCAoSU1XRCksIGVzIG5lY2VzYXJpbyBxdWUgbGEgaW1hZ2VuIHRlbmdhIHVuYSBmb3JtYSBjdWFkcmFkYS4gRGFkbyBxdWUgbXVjaGFzIGltw6FnZW5lcyBubyBzb24gY3VhZHJhZGFzLCBlcyBuZWNlc2FyaW8gY29udmVydGlybGFzIGFudGVzIGRlIGFwbGljYXIgZWwgYWxnb3JpdG1vLgoKQSBjb250aW51YWNpw7NuLCBzZSBwcmVzZW50YSB1bmEgZnVuY2nDs24gZGUgcHJlLXByb2Nlc2FtaWVudG8sIHF1ZSBhanVzdGEgY3VhbHF1aWVyIGltYWdlbiByZWN0YW5ndWxhciBhIHVuIHRhbWHDsW8gY3VhZHJhZG8sIG1hbnRlbmllbmRvIHN1cyBwcm9wb3JjaW9uZXMgb3JpZ2luYWxlcyBhbCBhZ3JlZ2FyIHJlbGxlbm8gKGZvbmRvIGJsYW5jbykgc2kgZXMgbmVjZXNhcmlvLiBFc3RhIHRyYW5zZm9ybWFjacOzbiBhc2VndXJhIHF1ZSBsYSBpbWFnZW4gc2VhIGNvbXBhdGlibGUgY29uIGVsIGFsZ29yaXRtbyBJTVdELgoKYGBge3J9CmhhY2VyX2N1YWRyYWRhX3BvdGVuY2lhXzIgPC0gZnVuY3Rpb24oaW1hZ2VuKSB7CiAgbl9maWxhcyA8LSBkaW0oaW1hZ2VuKVsxXQogIG5fY29sdW1uYXMgPC0gZGltKGltYWdlbilbMl0KICAKICBudWV2b190YW1hbm8gPC0gbWF4KG5fZmlsYXMsIG5fY29sdW1uYXMpCiAgCiAgc2lndWllbnRlX3BvdGVuY2lhXzIgPC0gMl5jZWlsaW5nKGxvZzIobnVldm9fdGFtYW5vKSkKICAKICBpbWFnZW5fY3VhZHJhZGEgPC0gYXJyYXkoMCwgZGltID0gYyhzaWd1aWVudGVfcG90ZW5jaWFfMiwgc2lndWllbnRlX3BvdGVuY2lhXzIsIGRpbShpbWFnZW4pWzNdKSkgCiAgCiAgaW1hZ2VuX2N1YWRyYWRhWzE6bl9maWxhcywgMTpuX2NvbHVtbmFzLCBdIDwtIGltYWdlbgogIAogIHJldHVybihpbWFnZW5fY3VhZHJhZGEpCn0KCmBgYAoKCkNhcmdhciBpbWFnZW5lcyBlbiBsYSBmdW5jacOzbiBjdWFkcmFkYQoKCgpBcGxpY2Ftb3MgbGEgZnVuY2nDs24gZW4gMyBmb3RvcwpgYGB7cn0KZm90b3NfY3VhZHJhZGFzIDwtIGxhcHBseShpbWFnZW5fbm9pc2UsIGhhY2VyX2N1YWRyYWRhX3BvdGVuY2lhXzIpCmBgYAoKSW1hZ2VuIE9yaWdpbmFsCgpgYGB7cixlY2hvPUZBTFNFfQppPTUKRUJJbWFnZTo6ZGlzcGxheShJbWFnZShpbWFnZXNbW2ldXSwgY29sb3Jtb2RlID0gJ0NvbG9yJyksIG1ldGhvZCA9ICdyJykKCmBgYAoKCgpgYGB7cixlY2hvPUZBTFNFfQpmb3IoaSBpbiBjKDE6MykpewpwcmludChuYW1lcyhmb3Rvc19jdWFkcmFkYXMpW2ldKQpwcmludCgnICcpCkVCSW1hZ2U6OmRpc3BsYXkoSW1hZ2UoZm90b3NfY3VhZHJhZGFzW1tpXV0sIGNvbG9ybW9kZSA9ICdDb2xvcicpLCBtZXRob2QgPSAncicpCn0KYGBgCgpDdWFuZG8gdXNvIGZvdG9zIGRlIG1heW9yIGNhbGlkYWQgc29sbyBhY2VwdGEgaGFzdGEgMiwgY29uIDMgYXBhcmVjZSBlc3RvOgpFcnJvcjogdmVjdG9yIG1lbW9yeSBsaW1pdCBvZiAxNi4wIEdiIHJlYWNoZWQsIHNlZSBtZW0ubWF4VlNpemUoKQoKCgpFbCB1bWJyYWwgInVuaXZlcnNhbCIsIHByb3B1ZXN0YSBwb3IgRG9ub2hvIHkgSm9obnN0b25lLCBlcyB1biBtw6l0b2RvIHV0aWxpemFkbyBlbiBsYSBlbGltaW5hY2nDs24gZGUgcnVpZG8gZW4gc2XDsWFsZXMgZSBpbcOhZ2VuZXMgbWVkaWFudGUgdHJhbnNmb3JtYWRhcyBkZSB3YXZlbGV0LiBFc3RhIGVzdHJhdGVnaWEgY2FsY3VsYSBlbCB1bWJyYWwgYXBsaWNhZG8gYSBsb3MgY29lZmljaWVudGVzIGRlIHdhdmVsZXQgZW4gZnVuY2nDs24gZGVsIHRhbWHDsW8gZGUgbGEgc2XDsWFsIHkgdW5hIGVzdGltYWNpw7NuIGRlbCBuaXZlbCBkZSBydWlkby4gTGEgZsOzcm11bGEgZGVsIHVtYnJhbCAidW5pdmVyc2FsIiBlcyAkJCBcc2lnbWEgXHNxcnR7MiBcbG9nIG59JCQgZG9uZGUgJFxzaWdtYSQgZXMgdW5hIGVzdGltYWNpw7NuIGRlbCBydWlkbyB5IG4gZXMgZWwgbsO6bWVybyBkZSBtdWVzdHJhcyBvIGVsZW1lbnRvcyBkZSBsYSBzZcOxYWwuCgoKCgpgYGB7cixlY2hvPUZBTFNFfQpwcm9jZXNhcl9pbWFnZW5fd2F2ZWxldCA8LSBmdW5jdGlvbihmb3RvLCB0aXBvID0gImhhcmQiLCBwb2xpY3kgPSAidW5pdmVyc2FsIikgewogIGx3ZCA8LSBsYXBwbHkoMTozLCBmdW5jdGlvbihjYW5hbCkgewogICAgd2F2ZXRocmVzaDo6aW13ZChmb3RvWywsY2FuYWxdKSAgCiAgfSkKICAKICAjIDIuIEFwbGljYW1vcyBlbCB1bWJyYWwgYSBsb3MgY29lZmljaWVudGVzIGRlIGxhIHRyYW5zZm9ybWFkYSB3YXZlbGV0CiAgbHdkX3RocmVzaG9sZCA8LSBsYXBwbHkobHdkLCBmdW5jdGlvbihjYW5hbF93ZCkgewogICAgbml2ZWxlcyA8LSBjYW5hbF93ZCRubGV2ZWxzCiAgICB3YXZldGhyZXNoOjp0aHJlc2hvbGQoY2FuYWxfd2QsIGxldmVscyA9IDM6KG5pdmVsZXMtMSksIHR5cGUgPSB0aXBvLCBwb2xpY3kgPSBwb2xpY3kpCiAgfSkKICAKICAjIDMuIEFwbGljYW1vcyBsYSB0cmFuc2Zvcm1hZGEgd2F2ZWxldCBpbnZlcnNhIGEgY2FkYSBjYW5hbCB1bWJyYWxpemFkbwogIGlsd2QgPC0gbGFwcGx5KGx3ZF90aHJlc2hvbGQsIGZ1bmN0aW9uKGNhbmFsX3VtYnJhbGl6YWRvKSB7CiAgICB3YXZldGhyZXNoOjppbXdyKGNhbmFsX3VtYnJhbGl6YWRvKSAgIyBUcmFuc2Zvcm1hZGEgd2F2ZWxldCBpbnZlcnNhCiAgfSkKICAKICAjIDQuIFJlY29uc3RydWlyIGxhIGltYWdlbiBjb21iaW5hbmRvIGxvcyB0cmVzIGNhbmFsZXMgcHJvY2VzYWRvcwogIGltYWdlbl9yZWNvbnN0cnVpZGEgPC0gYWJpbmQ6OmFiaW5kKGlsd2RbWzFdXSwgaWx3ZFtbMl1dLCBpbHdkW1szXV0sIGFsb25nID0gMykKICAKICAjIENvbnZlcnRpbW9zIGxhIGltYWdlbiByZWNvbnN0cnVpZGEgZW4gdW4gb2JqZXRvICdJbWFnZScgZGUgRUJJbWFnZQogIGltYWdlbiA8LSBJbWFnZShpbWFnZW5fcmVjb25zdHJ1aWRhLCBjb2xvcm1vZGUgPSAnQ29sb3InKQogIAogIHJldHVybihpbWFnZW4pCn0KCmBgYAoKKipJbWFnZW5lcyBzaW4gcnVpZG8qKgpgYGB7cixlY2hvPUZBTFNFfQojbWVtLm1heFZTaXplKDMyICogMTAyNF4zKSAKZm9yKGkgaW4gYygxOjMpKXsKcHJ1ZWJhPC1wcm9jZXNhcl9pbWFnZW5fd2F2ZWxldChmb3Rvc19jdWFkcmFkYXNbW2ldXSwgdGlwbyA9ICJoYXJkIiwgcG9saWN5ID0gInVuaXZlcnNhbCIpCnByaW50KG5hbWVzKGZvdG9zX2N1YWRyYWRhcylbaV0pCkVCSW1hZ2U6OmRpc3BsYXkocHJ1ZWJhLCBtZXRob2QgPSAncicsIHRpdGxlID0gJ0ltYWdlbiBXYXZlbGV0IHNpbiBydWlkbycpCn0KYGBgCgoKCgoKKlJ1aWRvIGdhdXNzaWFubyoKYGBge3IsZWNobz1GQUxTRX0KbGlicmFyeShtYWdpY2spCnBydWViYTwtcHJvY2VzYXJfaW1hZ2VuX3dhdmVsZXQoZm90b3NfY3VhZHJhZGFzW1sxXV0sIHRpcG8gPSAiaGFyZCIsIHBvbGljeSA9ICJ1bml2ZXJzYWwiKQppbWdfcmFzdGVyIDwtIGFzLnJhc3RlcihwcnVlYmEpCmltZ19tYWdpY2sgPC0gaW1hZ2VfcmVhZChpbWdfcmFzdGVyKQoKYGBgCgoKSW1hZ2VuIGNvbiBydWlkbyBHYXVzc2lhbm8gb3JpZ2luYWwKCjEuIEltYWdlbiBDb3J0YWRhIHNpbiBmaWx0cm8KCjIuIEFqdXN0YSBlbCBjb250cmFzdGUgZGUgbGEgaW1hZ2VuLCBpbmNyZW1lbnRhbmRvIGxhIGRpZmVyZW5jaWEgZW50cmUgbG9zIHDDrXhlbGVzIGNsYXJvcyB5IG9zY3Vyb3MuCgozLiBJbWFnZW4gY29uIGZpbHRybyBwYXNvIGFsdG8KCgpgYGB7cixlY2hvPUZBTFNFfQppbWFnZW5fY29ydGFkYTwtaW1hZ2VfY3JvcChpbWdfbWFnaWNrLCAiMTMwMHgxMjAwIikKCmtlcm5lbF9wYXNvX2FsdG88LW1hdHJpeChjKDAsLTEsMCwtMSw1LC0xLDAsLTEsMCksbnJvdz0zLG5jb2w9MykKCmZpbHRyYWRhX2FsdG88LWltYWdlX2NvbnZvbHZlKGltYWdlbl9jb3J0YWRhLGtlcm5lbF9wYXNvX2FsdG8pCgpjb25jb250cmFzdGU8LWltYWdlX2NvbnRyYXN0KGltYWdlbl9jb3J0YWRhKQoKaW1hZ2VuX2NvcnRhZGEgPC0gaW1hZ2VfYW5ub3RhdGUoaW1hZ2VuX2NvcnRhZGEsICJJbWFnZW4gQ29ydGFkYSIsIHNpemUgPSA1MCwgY29sb3IgPSAid2hpdGUiLCBsb2NhdGlvbiA9ICIrMTArMTAiKQpjb25jb250cmFzdGUgPC0gaW1hZ2VfYW5ub3RhdGUoY29uY29udHJhc3RlLCAiQ29uIENvbnRyYXN0ZSIsIHNpemUgPSA1MCwgY29sb3IgPSAid2hpdGUiLCBsb2NhdGlvbiA9ICIrMTArMTAiKQpmaWx0cmFkYV9hbHRvIDwtIGltYWdlX2Fubm90YXRlKGZpbHRyYWRhX2FsdG8sICJGaWx0cmFkYSBBbHRvIiwgc2l6ZSA9IDUwLCBjb2xvciA9ICJ3aGl0ZSIsIGxvY2F0aW9uID0gIisxMCsxMCIpCgojIENvbWJpbmFyIGxhcyBpbcOhZ2VuZXMgaG9yaXpvbnRhbG1lbnRlCmltYWdlbl9jb21iaW5hZGEgPC0gaW1hZ2VfYXBwZW5kKGMoaW1hZ2VuX2NvcnRhZGEsIGNvbmNvbnRyYXN0ZSwgZmlsdHJhZGFfYWx0bykpCgpFQkltYWdlOjpkaXNwbGF5KEltYWdlKGZvdG9zX2N1YWRyYWRhc1tbMV1dLCBjb2xvcm1vZGUgPSAnQ29sb3InKSwgbWV0aG9kID0gJ3InKQoKIyBNb3N0cmFyIGxhIGltYWdlbiBjb21iaW5hZGEKcHJpbnQoaW1hZ2VuX2NvbWJpbmFkYSkKYGBgCgoKCgojIyBGdW5jacOzbiBkZW5vaXNlLi4uCgojIENvbmNsdXNpb25lcwo=